5. Rules used inside the code.
Several rules were used during DemoGL development. It's recommended you follow these rules when
you want to add code to the library or want to let us add your additions to the codebase. Some are
basic coding practises, others are not that common and can be sounding a little over the top. These rules
are very important when you want to contribute code to the codebase.
- Programming rules
- Header file usage
- Every .cpp file should have a comparing header file, .h. All declarations, defines,
typedefs etc are made in that headerfile.
- No #defines, class definitions, struct definitions and declarations of types are
allowed in .cpp files. Use the .h file for that. Exceptions are #defines of macro's,
however try to avoid macro's.
- Preferably include header files in a global include file so it can be pre-compiled,
plus this solves include loop errors.
- Sections in the headerfile should be properly separated by commentblocks.
- Place only headerfile information of related material in the same headerfile.
- Name the headerfile after the .cpp name.
- Sort class methods ascending.
- Always use a #ifndef _HEADERNAME #define _HEADERNAME #endif construction to avoid
multiple includes of the same file.
- Source file usage
- Don't put code in headerfiles, no matter how small the code is, even in class definitions.
Place code in .cpp files, the source files.
- Sections in the sourcefile should be properly separated by commentblocks.
- Place only code related to eachother in one single .cpp file. Split up files if code is not
tight related.
- Choose a good name for the .cpp file, related to the code inside. Prefix every filename with
'dgl_dll'.
- Classes
- No public members are allowed. Use only private members. When you need a fast 'object'
with just variables, use a struct.
- No initialisation of members is allowed in the constructor declaration. Initialise members
inside the constructor body. This way it's more extensible, and more readable because you
can add comments.
- Keep Get/Set methods related to the values they get/set.
- Use classes when you want to code a topic that is best served with OO programming.
Don't use classes when a functional approach would be better.
- Always implement virtual functions in base classes.
- Comments
- Add lots of comments and keep them accurate and describing. A reader can interpret what
for() does, not why it's there and why it's constructed that way.
- Every method/function should have a commentheader that describes what the function/method does,
even if the method is only one or two lines big. If possible, describe the parameters sent and
returnvalues possible. If possible describe key functionality so the reader of the code is
not overwhelmed by the code.
- Add comments with preprocessor directives.
- Functions / methods
- Functions and methods can have multiple exit points. Some people think multiple exit points
in a function is bad, but opinions differ on this. A function/method should eliminate cases
when the caller shouldn't be executing the core functionality of the function/method. Always
ask you the question: should the caller still be in this function? if not: exit.
- Functions and methods should follow the following order of codeblocks:
- Function/method header
- Local variable declaration
- Local variable initialisation
- codeblocks
This way you always know where to look when a local variable is not initialized etc. Of course it's
not always necessary to initialise variables at the top of the function/method. But it keeps
logical blocks together in a function so less puzzling has to be done when a reader wants to find
out what's going on.
- No block locals are allowed. Always declare your variables with function scope, not block scope
inside your function/methods.
- No direct initialisation of variables is allowed. First declare, then initialize. This way you
can keep logical parts of a function separated from each other, even if this is slightly less
efficient.
- Don't return pointers to local declared memory blocks. Concern the following:
{
char sFoo[MAX_STRING_LENGTH];
sprintf(sFoo,"bla");
...
return sFoo;
}
sFoo is declared on the stack in the stackframe of this method/function. Returning it to the
caller will cause trouble, when the caller stores this pointer in another variable. Always
return pointers to allocated memory.
- Copy structures which are passed by reference into local structs when using the data, so caller
always frees the data passed, and is responsible for the data passed, never the callee. People who
write COM objects are used to this.
- Keep methods/functions atomic in error handling. Allthough it seems nice to report errors back to
the caller, you don't know which method/function will call you in the future, don't create a
system that depends on the caller. Report success/failed signals, using SYS_OK and SYS_NOK flags,
like in COM and eventually an errorcode if necessary, but keep errorcode general and not caller specific.
- It's not always bad to keep a long function in tact, as long as the code is generic and just a lot of
the same. DemoGL contains a few long, but boring functions. Not a lot is going on, just a lot of the
same. Message handlers are longer than you'd expect and the timeline event execution routine is very
long. The functions could have been split up in a lot of smaller functions, but that would create
extra overhead and not that much more readability, due to the fact that the overview of what's going
on is gone when using a lot of small, albeit the same, functions who are only used by one other function.
It's wise to split up functions that tend to do a lot of work which is not a repetitive collection of
the same actions over and over again. You might now think that there is no loop whatsoever in the source
but the code I'm refering to is a long list of command handler routines, which are semantically a lot
alike but differ in syntax and errorreporting.
- Place the type of the returnvalue of a function/method on the line above the method/function header.
Example:
char
*CClass::Foo(int iBar)
{
// some code
return pszBla;
}
- Place function header declarations in the headerfile and extern functionheaders which should be included in
more than one .cpp file.
- Don't use extern "C" {} when exporting API functions. The API functions exported that way are v1.2 functions,
and declared that way for backwards compatibility.
- Never return a pointer to a block of memory to a caller that is not inside the library, and there is no free
function. Because memory allocated inside the dll cannot be freed by code outside the dll, you can't pass back
pointers to memory, allocated inside the dll, that should be cleaned up by the caller, outside the dll. Include
a mechanism to free the memory when doing so, as is done with the filepointer return/freeing in the DemoGL
API.
- Statement layout
-
- Naming scheme
- The code is written using a namingscheme for all variables, class definitions, structs and constants.
This scheme is adjusted a bit over time, so some inconsistencies can still be in the sourcecode. Be
sure you adapt this coding scheme to all your code you want to add to the library. It might be a hell
at first when using this coding scheme, but after a while it will pay off. Of course this naming scheme
can be less good than the one you probably use yourself for ages, however it's not the quality of the
naming scheme that matters, but the presence of it. Too much code is written without any naming scheme
and for Open Source software it's very important to keep one consistent naming scheme that is mandatory
for all developers. This scheme is by no means complete. If you think it should be adjusted or it lacks
some definitions, step forward and suggest the addition/modification.
The following naming scheme is defined. If you wonder why variables and code is written the way it is,
you'll find your anwsers probably here. It's a subset of Hungarian coding, with modifications.
- #defines are capitalized. Example: REGKEY_CURRENTUSER_SUB
- API methods are prefixed with DEMOGL_. Example: DEMOGL_ConsoleLogLineV()
- API functionnames should be build up like: DEMOGL_[objectaffected][action]. Example: DEMOGL_TextureLoad().
- API constants are prefixed with DGL_. Example: DGL_VF_VSYNC
- Class names are prefixed with 'C'. Example: CEffect
- Struct names are prefixed with 'S'. Example: SSoundElement3DAttributes
- Enum names are prefixed with 'E'. Example: ECmdToken
- Class en struct member variables are prefixed with 'm_'. Example: m_iToken
- Global (library scope) variables are prefixed with 'm_g'. Example: m_gpDemoDat
- All member variables and local variables are prefixed with a character string in lowercase
reflecting the type. This prefix is placed after the member variable prefix 'm_' and
after the global prefix 'm_g'. The list of type prefixes is:
- 'i' for integer. Example: int iCounter;
- 'f' for float. Example: float fX;
- 'dw' for DWORD. Example: DWORD dwBassFlags;
- 'l' for long. Example: long lFileLength;
- 'p' for pointer. Example: char *pCurrent;
- 's' for charstring. Example: char sFoo[MAX_STRING_LENGTH];
- 'psz' for pointer to zero terminated charstring. Example: char *pszName;
- 'b' for boolean. Example: bool bHasTexture;
- 'h' for handle. Example: HWND hWnd;
- 'by' for byte. Example: byte m_byKey1;
- 'cs' for Critical Section Mutex. Example: CRITICAL_SECTION m_csSystemStateMutex;
There are legacy declarations of pointers to strings that should be declared as psz's instead of just
'p's. Besides these definitions, you can combine them. For example a pointer to integer can be
declared as 'piFoo', instead of just 'p'. You will also notice the several custom types declared
in DemoGL's sourcecode. For example the channel objects in the soundsystem are prefixed with 'ch'.
Most arrays, when declared local or as member, are prefixed with 'arr'. An array of channelobjects
which is a member will then become: m_arrchFoo[MAXVALUES].
- All names of membervariables and other variables should start with a capitol character after the prefixes.
Also put a capitol at every wordstart in the variable name. Thus: m_csSystemStateMutex, instead of
m_csSystemstatemutex.
- Avoid understores in variable names and function/methodnames. Use them only as separator to exclude parts
in the name.
- Use C++ commenting syntax when possible.
- In API functionheaders, define variables as 'const' when possible. Keep in mind that caller should be
able to modify objects passed to a function.
- Never use hardcoded values in the code. Always define a constant using the right scheme.
- Adjust the scheme, don't alter it. Custom types ask for custom prefixes. Add these prefixes to the
scheme, like a pointer to a SSoundElement3DAttributes struct should be called: m_psea3DAttributes. Keep these
adjustments consistent, that's the reason why they should be added to the scheme when used.
- Dont include type info in the name. Because the prefix already tells the reader the type of the variable,
it's not necessary to include also words like 'String' or 'Str' as the 's' or 'psz' will already indicate
it's a string.
- Keep names of variables descriptive of what they contain. If possible, always make the name as descriptive
as can be. 'bFlag' is nice, but what kind of flag?
- Keep method and functionnames descriptive of what they do. A reader should understand what a function or
method does when he sees the name of the method/function. If the function does more than that, it should be
split up or the name should change.
- Don't be afraid of long names. Some people avoid long variablenames and end up using complete unworkable
names for variables. One of the longes names used is: m_iDeltaVolumeOutsideOuterProjectionCone, a member of
SSoundElement3DAttributes. Making this name shorter wouldn't tell the reader what's inside the name.
- Use i, j, k and l for loopcounters. By unwritten definition, in C and C++, loopcounters in for loops and
whileloops are named 'i' and nested loops use variablenames one larger as the parent loop, like 'j' or 'k'.
- Internal functionality Usage
- "Eat your own dogfood!". Well, when possible. When you have the choice to call a library API method or an internal
function that is called by the API method, choose the API method. This way you keep the internal code more bugfree.
The API is not going to change that often, if it will ever change, code that relies on internal functions will
probably become unstable and buggy when the internal workings of an API method will change (i.e.: how the API
method works. It still provides the same functionality). You also have the possibility to test API code right
away.
DemoGL contains some handy pieces of code that are very useable for developers of the library and need explanation
when you want to read and/or modify the code. Below is a list of codepieces and what they do. Some are 3rd party,
some are developed from scratch for DemoGL. Use these as much as possible instead of your own code. These codepieces
are tested thouroughly. When a piece of code is limited to your needs, adjust it. Don't include another piece of
code that does almost the same but something more, adjust the current codepiece.
- CStdString. This is a 3rd party stringclass written by Joe O'Leary. It's used often inside DemoGL
and it should be used instead of char sFoo[value] when possible. There are several occasions when
CStdString is not useful, for example when exporting a struct with a char buffer to a caller outside the
library. CStdString is a C++ class similar to CString in MFC but more efficient.
- Low level debugger. DemoGL contains it's own low level debugger. When you include the _DGLDEBUG
constant in the C++ preprocessor definitions in Project Settings, code will be generated using the
low level debugger functions. The low level debugger will open a file and write strings to that file
when ordered. The open/close functionality is included in the DllMain function, but commented out.
If you want to debug parts of the library and you want to use the low level debugger, uncomment these
lines and call the low level debug function of your needs. This low level debugger can be adjusted
with a console instead of a file logger. The win32 API contains code to open a console window for
logging purposes. The low level debugger is not thread safe at the moment but flushes data directly
to disk when ordered to write data to the file. Keep in mind when using CreateThread() instead of
the _beginthreadex() calls as DemoGL uses now, the CRT functions used in the low level debugger can
be thread unsafe and can cause corruption of data. See CreateThread() documentation for details.
- CSysConsole. DemoGL contains a console for debugging purposes. You can log data to the console,
which can scroll back and forth inside a buffer of userdefinable length and width, or retrieve system
info like timeline status, running effects etc. Use the API functions DEMOGL_ConsoleLogLine() and
DEMOGL_ConsoleLogLineV() to log debuginformation in the console. This method is very fast, as the
low level debugger is not. The console debugging method can be a little tricky when you are debugging
a screensaver you wrote with DemoGL, because any keypress will exit DemoGL when running as a screensaver.
The systemconsole contains also the debugoverlay that is useable during runtime which displays a subset
of the contents of the console. DemoGL uses the console to log systeminformation. When you specify
QUICKBOOT as true when starting your application, DemoGL will however only log errors, and no system
information is logged.
Top
6. The DemoGL_DLL VC++ project.
The project contains several directories in the project fileview explorer, which we will call 'folders' to avoid conflicts with
'directory' on disk. The folders are:
- Source Files. This folder contains all .cpp files
- Header Files. This folder contains all .h files
- Resource Files. This folder contains all files included as resources in the library. See the Resources Tab.
- Libs. This folder contains all .lib files linked with the library. This is done so a change of a lib
to a newer version will cause a recompile of the library.
- Source docs. This folder contains all documentation about the sourcecode
- External Dependencies. This folder is added by VC++ and contains all files needed for compilation.
The folders 'Source Files' and 'Header Files' contain besides the DemoGL sourcefiles, also a '3rdParty_H' (in Header Files)
and '3rdParty_CPP' (in Source Files), folder, which contain the 3rd party code included in DemoGL. All code inside these
folders is written by others and contains and should keep the names of the authors.
On disk, the directory structure is slightly different. In the rootdirectory of the project are the necessary project files
for VC++, a makefile plus several directories with the files. Below is a list with eventually subdirs and what they contain:
- CPP. The root directory for the directories that contain the .cpp files. This directory contains the following
directories:
- DemoGL, where all .cpp files written for DemoGL are placed.
- Misc, where all 3rd party .cpp files are placed.
- Debug. The directory where the result of a 'debug' build is placed by VC++.
- Files needed for distribution. In here are the files needed for the SDK. These files are not
included in the project, but are necessary to run DemoGL applications.
- Include. The root directory for the directories that contain the .h files. This directory contains the
following directories:
- DemoGL, where all .h files written for DemoGL are placed.
- Distribution, where all the .h files, needed for the SDK are placed, like DemoGL_DLL.h etc.
- JpegLib, where all files for the jpeglib are placed. Could be moved to Misc.
- Misc, where all 3rd party .h files are placed.
- Libs. The directory for all .lib files needed to build the DemoGL library.
- Release. The directory where the result of a 'release' build is placed by VC++.
- ReleaseVC50. The directory where the result of a 'WIn32 VC50' build is placed by VC++. VC++ 6.0 has the
feature to build .lib import libraries for VC++ 5.0. If you want to build an importlib for VC++ 5.0, build the
'Win32 VC50' build using 'Set active configuration' in VC++ in the Build menu.
- Resources. The directory where all files included as resources are stored.
- Source docs. The directory where the documentation of the source code is stored.
6.1. Resources
DemoGL contains several resources. These resources are dialog templates and binary files. Select the 'Resources' tab in the
project explorer inside VC++ to view the list of resources available. These resources are stored in resources.rc, the
file that will be compiled by VC++ which will result in a binary blob that will be linked with the DLL. In resource.h, the
controls used in the dialogs are defined with #defines. This file is generated by VC++ and altered to define two standard
values for two elements: IDI_ICON, the application's icon, and IDB_APPLOGO, the grey bar at the left of the startup dialog
which can be customized to a provided picture. These two elements have standarized values and shouldn't be altered.
The resources contained in DemoGL are the following:
- "JPG". This is a custom resourcetype and contains one object, a lensflare in JPG format, which is used in the
About dialog. Total size is 13KB.
- "ZIPPEDTGA". This is a custom resourcetype and contains one object, a 'compressed' TGA image of the DemoGL logo.
This TGA image contains an alpha channel and is used in the About dialog. It's compressed with ZLib to keep the size
small. Total size is 11.4KB.
- Bitmap. This resourcetype contains one object, a bitmap that is placed as the application logo in the grey bar
when the application itself isn't providing a resource with ID IDB_APPLOGO (i.e. 140). Total size is 5KB.
- Dialog. This resourcetype contains two objects: the About dialog template and the Startup dialog template.
- Icon. This resourcetype contains one object, the default Icon for the application if the application itself
isn't providing a valid icon with ID IDI_ICON (i.e. 110).
Some will now argue that 13 + 11.4 + 5 = 29.4KB could have been saved by eliminating the image resources. These resources are
included for the About dialog to show off something special. The 5KB for the bitmap in the applogo image holder is needed
otherwise the control will not show up in the dialog template.
6.2. File descriptions
Below are descriptions of all the files in the project written for DemoGL, the dgl_dll*.* files. Each file contains
a short description of its contents plus some key information about the logic of the code stored in the file. These
descriptions are by no means an intention to be an in depth view of how every detail works. The code contains enough
comments and the code itself is set up as clean as possible so these descriptions are helping you where to take off
when reading the sourcecode.
6.2.1. Source Files.
- dgl_dllbootutilfuncs.cpp
- This file contains all functions that are doing several blocks of the booting process of a DemoGL powered
application. Among the several OpenGL window setup related routines are also the decrypting routines to
decrypt data crypted with Cryde. No extra information is needed, every routine is explained in detail in
the sourcecode.
- dgl_dlldemodat.cpp
- This file contains the implementation of the CDemoDat class. Because this class is mainly an internal datastore,
it's quite big with a lot of small get/set methodcombinations. It's wise to use the File Outline functionality
of VC++ 6.0 in this file. (browse toolbar). Keep an eye on the ticker routines, which are stored here and set
up the system high resolution timer, which is kept inside CDemoDat, plus the overlay switch on/off routines.
When a user toggles an overlay (console, debug overlay, FPS counter), the console has to clean the buffer
using no scissoring otherwise a demo with scissoring will contain flickering elements of the overlay.
- dgl_dlleffectbase.cpp
- This file contains the base class implementation of the CEffect class which is used by applications to derive
their effect classes from and is used internally as reference class to access effectobjects. The class itself
only contains implementations of the virtual functions.
- dgl_dlleffectstore.cpp
- This file contains the implementation of the CEffectStore class. This class is used to store an effect object
reference plus related information inside DemoGL. When an application calls DEMOGL_EffectRegister(), a new
instance of CEffectStore is created and filled with the information provided. CEffectStore objects are stored
as a single linked list with a head and tail pointer, stored and defined in dgl_dllmain.cpp.
- dgl_dllendsystem.cpp
- This file contains the code that is executed to clean up all the left overs when DEMOGL_AppRun() returns.
The routine cleans up all data allocated after DEMOGL_AppRun() was called. Data allocated in DllMain() is not
deallocated because that is deallocated when the process itself detaches itself from the library, see DllMain()
in dgl_dllmain.cpp for details.
- dgl_dllextensions.cpp
- This file contains the implementation of the CExtensions class. This class contains at runtime booleans for
a long list of extensions, reflecting their presence in the ICD used.
- dgl_dllfifoqueue.cpp
- This file contains the implementation of the CFifo queue class. This class implements a first-in-first-out
queue for CEffect references. This queue is used by the Prepare thread when it finds an effect has to be
prepared. Because in the Prepare thread, the OpenGL rendercontext is not available so calling these effect's
Prepare() method is useless or will render the code inside that method useless when it does something with
OpenGL, so the Prepare() thread places the effect in the Fifo queue instead. The actual placement of the
effect inside the queue is done inside DoExecuteTimelineEvent(), when it's called by the Prepare thread.
When DoExecuteTimeLineEvent places an effect inside the fifo queue, it will send a message, WM_DEMOGL_PREPAREEFFECT,
to the main thread. The main thread will, when receiving the message, pick up the stored effects in the
fifo prepare queue and will actually call the Prepare() methods of the effects stored in the queue. The queue
has a fixed length of 100 slots which should be more than enough, when the main thread and the Prepare thread
work in tandem and get equal amount of CPU time.
- dgl_dllguicontrol.cpp
- This file contains the implementation of the base class CGuiControl that is useable as a baseclass to derive
guicontrols from, like the progressbar is a derived class from CGuiControl. The class doesn't contain any render
logic, just data and get/set pairs.
- dgl_dllkernel.cpp
- This file contains the core control functions of the system when it's running and starting. In this file you'll find
the Main message handler and the message pump, the effect register routine and the frame render routines. The message
pump is called 'KernelLoop()'. The main message handler is called 'MainMsgHandler()'. These two routines are key
elements in the system. The MainMsgHandler routine contains key code how the system state is controlled and how
to react on external- or internal generated events. The frame render routines are RenderFrame() and RenderLayerFrames().
RenderFrame is the main routine which controls rendering of frames. It's called by KernelLoop whenever possible, to
get the highest framerate possible. RenderFrame decides what to render: console or effects. When effects have to be
rendered it calls RenderLayerFrames() to render all effects on all layers using the effects's RenderFrame() methods.
- dgl_dlllayer.cpp
- This file contains the implementation of the CLayer class. This class contains all data of a layer in DemoGL.
When an effect is started on a layer, an instance of the CLayer class is created to represent that layer. All
layer objects are stored in a double linked list with a head pointer, stored and defined in dgl_dllmain.cpp
- dgl_dlllayerutilfuncs.cpp
- This file contains all utility functions that work on the layer store. All layer store manager functions are stored
in this file. Also the per layer object functions are stored here like hide or show a layer. The tricky code is in
the double linked list logic.
- dgl_dlllowleveldebugger.cpp
- This file contains the low level debugger implemented in the library. The low level debugger is a set of utility
functions that work with a logfile. This logfile is initially opened in the DllMain() function in dgl_main.cpp.
When you uncomment the open/close calls of the logfile, DemoGL will log data in the logfile which is placed in the
current directory of the application. You can call a utilty function of the low level debugger from anywhere in
the library code. Be sure to include _DGLDEBUG with the preprocessor directives in the project settings: when
_DGLDEBUG is defined, all functions of the low level debugger have functionbodies and will thus be executed when
called. When you include calls to the low level debugger functions in the library source code and _DGLDEBUG is
not specified with the build, for example with a release build, the calls to the low level debugger will be calls
to empty function bodies and will be removed by the optimizer in VC++. The low level debugger contains functions
to convert a window message to text, log strings and other miscellaneous functions that come in handy when doing
logging during runtime.
- dgl_dllmain.cpp
- This file contains one function, the DllMain() function. This function is the entrance function for win32 to attach
the DLL to the process or thread that loaded the library, directly or indirectly. DllMain() contains code for the four
situations that this function can be called: attach to thread or process or detach from thread or process. Only attachment
and detachment to a process is implemented, since this is what we want: an application using DemoGL is loading the DemoGL
DLL, most likely by VC++ generated stubcode, and will attach to it after the DLL is loaded. When an attachment happens,
the DLL can do initial setup of objects and internal structures. DemoGL will create it's internal objects and stores
and do very basic initialisation. After the application ends, thus after the WinMain() function is exited, DllMain() is
called again, now for the detachment from the application process. DemoGL will then remove the internal structures and
objects created in the attachment handler. After that call there shouldn't be any memory left that is still allocated.
When you monitor memory leaks using the win32 heap monitor functions be aware that when DEMOGL_AppEnd() is finished
the memory occupied by the elements allocated in the attachment of the library is still not freed and your memleak
sniffers will alert for memory leaks. You should place your sniffers after the detachment code in DllMain() to have
accurate memleak sniffing. This file contains besides DllMain() also the declaration of all the objects and structures
that have library scope. Place all library scope variable declarations in this file.
- dgl_dllprogressbar.cpp
- This file contains the implementation of the CProgressBar class. This class is derived from CGuiControl and the instance
of this class is used as the progress bar during boot/initialisation. DemoGL contains one progress bar object, declared
in DllMain(). It's declared with global scope to have any eventhandler in the system be able to make the bar move forward
when a certain task is completed. CProgressBar implements several visual types. It uses a mutex to protect it's internal
value it reflects in the bar for mutations by multiple threads. CProgressBar contains its own rendercode and this code
is called by the system console. Because the system console is an object on itself, it is not relying on system wide
global variables and gets a reference to the instance of the CProgressBar class when it's initialized. This is a slight
inconsistency when compared to the CDemoDat situation which is referenced throughout the system, also from the SoundSystem
object.
- dgl_dllscriptfuncs.cpp
- This file contains all script related functions. In this file are stored the lexical analyzer and the token parser plus
utility functions to ease parsing of the script. The code uses a token array with token objects which are filled by the
lexical analyzer Lex(). When Lex() has tokenized a line, this line is parsed and if possible transformed into a timeline
event and stored in the timeline at the right spot. The lex-parser tandem is very basic, for example the parser doesn't do
any syntax checking, this is done at runtime.
- dgl_dllsoundsystem.cpp
- This file contains the implementation of the CSoundSystem class, the CSoundChannel class, the CSoundElement class, the
CMP3Element class, the CMODElement class and the CSampleElement class. Besides these classes it contains all soundsystem
related utility functions and initialisation functions. DemoGL 1.3 uses BASS to produce sound and the soundsystem is
written to work as good as possible with BASS. To the outside world the soundsystem is a black box and the code that uses
the soundsystem is not aware BASS is used. This way other libraries, besides BASS, can be used to power the soundsystem
if BASS doesn't fulfil your needs. DllMain() will create an instance of CSoundSystem so DemoGL can produce sound using
this instance. Internally the sound system uses sound elements. Sound elements are instances of CMP3Element, CSampleElement
or CMODElement. These three classes are derived from the CSoundElement, which is the base class for sound elements inside the
sound system. These three classes contain the sound data of their type, which is MP3, Sample (wave) or MOD/S3M/IT/XM data.
All interaction on these elements is done by the sound system object. Inside the sound system, all sound elements are
stored in the sound element store. When a sound element is started, thus it is playing, an instance of the CSoundChannel
class is created. This instance is placed in the sound channel store. This sound channel object is the controlling instance
of the played sound. Thus, CSoundElement derived classes control the to be played sounddata, the CSoundChannel class
controls the playing sounddata.
- dgl_dllstartsystem.cpp
- This file contains the startroutines needed to start the application. It contains DEMOGL_AppRun() and related routines
plus the initialisation and prepare routines of the system which are called or started as a thread in the several phases
prior to the SSTATE_ARUN_KERNELLOOP state. DemoGL_AppRun calls routines that actually do the startup of the application
depending on the runtype DemoGL should run in. Depending on the runtype, the routine starts a startup dialog, opens
the main window and dives into the kernelloop. Keep in mind that InitApp(), InitSystem() and Prepare() are started as
threads by messagehandlers in MainMsgHandler() in dgl_dllkernel.cpp, to activate a state.
- dgl_dllstartupdialog.cpp
- This file contains the code for the startup/screensaver config dialog and the about dialog. The startup/screenaver config
dialog will read the settings from the registry if present, and fill the dialog with the settings provided by the developer.
It exits, filling the startup dat structure with the settings set and eventually calling the routine which stores the
settings in the registry. See the messagehandler of this dialog for details. The about dialog contains an OpenGL canvas
and rotates a quad with a DemoGL texture and is envmapped with a flare to give it some shine.
- dgl_dllsysconsole.cpp
- This file contains the implementation of the system console class CSysConsole. An instance of this console is created in
DllMain() in dgl_dllmain.cpp. The CSysConsole class controlls the console buffer, the rendering of the console buffer, the
header above the console buffer when the console is displayed, the rendering of the input prompt, the rendering of the
FPS overlay and the rendering of the debug overlay. The CSysConsole uses two textures with a font to render the characters
using two vertexarrays for every font texture one. There are two textures to have a 512x256 wide texture and still keep
the details on voodoo3's. The CSysConsole class does not contain the contents of the input prompt, it only renders it.
This is because the datainput is received outsite the console, in the message handler of DemoGL. CSysConsole uses a cyclic
buffer technique to store the console lines. This means that the current line where to write text on moves through the buffer
and wraps around at the end to start at the beginning. This way the buffer doesn't have to be copied when the window scrolls
up. The console contents depends on the position of the topline visible on screen. When the user scrolls in the console, this
topline moves up or down. It depends on this line if a newly logged line is visible or not. When the screen has to be redrawn,
the vertexarrays are refilled and rendered, otherwise the already filled arrays are rendered. The cyclic buffer logic can be
quite complex at first, especially in the calculations for the scrollbar in the console and which line is visible and which
isn't. Everything is commented as best as possible, but it might take a while to get through.
- dgl_dlltexture.cpp
- This file contains the implementation of the CPixelStore class and the CTexture class. The CTexture class contains all the
information for a texture inside DemoGL. All textures created with DemoGL are placed in the texture store. This is an
array with pointers to CTexture objects and contains MAXTEXTURES (defined in dgl_dllmain.h) slots and defined in dgl_dllmain.cpp
and initialized in DllMain().
This store is not a linked list to keep it as fast as possible because texture access is done very often in a 3D application
and it should be smooth and not be blocked by sloppy linked list search routines. The array lets code access a texture
object immediately. The CTexture class is mainly ment for storage and is prepared for texture compression and 3D textures.
This code is not implemented in v1.3x due to lack of OpenGL 1.2 support in Windows.
- dgl_dlltextureutilfuncs.cpp
- This file contains all utility functions related to the texture store or the texture object. It contains general texture
management functions and texture import routines to convert a loaded block of data into an RGBA bitmap. All functions are
prepared, when necessary, for 3D textures and texture compression. As said above, this is not yet implemented.
Currently JPEG, TGA BMP and DDS (DirectX Compressed) texture files are supported. When you add more formats, add your
import code to this file.
- dgl_dlltimelineevent.cpp
- This file contains the implementation of the CTimeLineEvent class and the CTLEParameter class. The CTLEParameter class is
used to hold script command parameter data. CTimeLineEvent classes are emited objects by the script parser and contain
the parsed script commands as data. Each CTimeLineEvent class contains all the script command parameters as CTLEParameter
objects in a datastore. All CTimeLineEvent objects are stored in a single linked list, the timeline event store,
ordered by TimeSpot Ascending, with a single head pointer, m_gpTimeLineHead, stored in dgl_dllmain.cpp.
- dgl_dlltimelineutilfuncs.cpp
- This file contains all the functions related to timeline events and the timeline event store. Besides the general add/delete
and other similar functions this file contains the largest routine inside DemoGL, the TimeLine Event executor routine
DoExecuteTimeLine(). This is in fact the runtime command executor for all objects and all commands. The routine contains
a note why the routine isn't split up in a couple of dozen smaller routines. It probably should be recoded with a pre-execution
syntax checker to eliminate runtime syntax checking as that's used now. Keep in mind that the massive amount of redundancy
has a reason: now every command is flexible in it's own execution without worrying the programmer about other commands sharing
this code. Because all code is stored in one routine, the overview is still very good, because nothing is splattered around
and because no code should be reused in other commands, splitting up the routine will not bring that of an much advantage.
- dgl_dlltoken.cpp
- This file contains the implementation of the CToken class. This class is used to store information about a single token
recognized by the Lex() routine in dgl_dllscriptfuncs.cpp. Lex will place every part of a script line between two ';' into
one CToken object. These objects are stored in an array with file scope inside dgl_dllscriptfuncs.cpp.
- dgl_dllutilfuncs.cpp
- This file contains the miscellaneous functions that couldn't be stored elsewhere. Most of the functions are helper functions
for objects inside DemoGL but are too small to get an own file. In this file are also the file I/O routines which could have
been split out of this file and should probably get their own file.
6.2.2. Header Files.
Below are the descriptions of the headerfiles used in the DemoGL sourcecode. Keep in mind that the DemoGL_*.h files are stored
in the Include\Distribution directory to keep them separated to make constructions of SDK's easier.
- DemoGL_Bass.h
- Sound system related header file for distribution purposes. Contains BASS specific flag definitions and structures
used by BASS. Use them with certain specific soundsystem related API routines. The reason for the BASS flags and not
a conversion of them to DemoGL own flags, and thus keep the stuff more sound library independant, is that when the
BASS api changes, the DemoGL own flags have to change as well. To avoid that, these flags and structs are kept in tact,
but the amount of them is kept to a minimum. When you port the CSoundSystem class to for example FMod, you should replace
this file with an FMod equivalent, which should be too hard.
- DemoGL_DLL.h
- The DLL include header to use DemoGL inside an application. Keep in mind that all API definitions are copied from the
library header files, as this file is not used in the compilation of the library. Keep in mind to copy modifications to
API functions or additions to the API functions to this file.
- DemoGL_Effect.h
- This file is the include file for using CEffect as a baseclass for effectclasses in applications. It's a copy of
dgl_dlleffect.h, but now usable for including in user applications. Keep the order of the methods the same in both
files. (read: don't alter them, unless you know what you're doing)
- DemoGL_Extensions.h
- This file contains the definitions of internal constants for all the OpenGL extensions recognized by DemoGL. Allthough
as much extensions as possible are added, still some are missing. The constants are used to check if an extension is
available and found by DemoGl.
- DemoGL_glext.h
- This file contains as much extension definitions as known today. All extensions from all popular card vendors are included.
This file is useful when you want to bind a functionpointer to an extension or use constants defined by an extension.
- dgl_dllbootutilfuncs.h
- Header file for dgl_bootutilfuncs.cpp.
- dgl_dlldemodat.h
- Header file for dgl_dlldemodat.h. Contains CDemoDat class declaration.
- dgl_dlleffect.h
- Header file for dgl_dlleffectbase.cpp. DemoGL_Effect.h is copied from this file. Contains CEffect class declaration.
- dgl_dlleffectstore.h
- Header file for dgl_dlleffectstore.cpp. Contains CEffectStore class declaration.
- dgl_dllendsystem.h
- Header file for dgl_dllendsystem.cpp
- dgl_dllextensions.h
- Header file for dgl_dllextensions.cpp. Contains CExtensions class declaration.
- dgl_dllfifoqueue.h
- Header file for dgl_dllfifoqueue.cpp. Contains CFifo class declaration.
- dgl_dllguicontrol.h
- Header file for dgl_dllguicontrol.cpp. Contains CGuiControl class declaration.
- dgl_dllkernel.h
- Header file for dgl_dllkernel.cpp.
- dgl_dlllayer.h
- Header file for dgl_dlllayer.cpp. Contains CLayer class declaration.
- dgl_dlllayerutilfuncs.h
- Header file for dgl_dlllayerutilfuncs.cpp.
- dgl_dlllowleveldebugger.h
- Header file for dgl_dlllowleveldebugger.cpp. Contains definitions for the logfile name and how deep the nesting of calllevels
can be for indenting in the logfile.
- dgl_dllmain.h
- Header file for dgl_dllmain.cpp. Contains all global scope constants definitions. Because DemoGL uses a lot of constants
instead of hard coded numeric values, most of these values are stored in this file. When you want to tweak or adjust something
in DemoGL, this is the place to start.
- dgl_dllprogressbar.h
- Header file for dgl_dllprogressbar.cpp. Contains CProgressBar class declaration.
- dgl_dllscriptfuncs.h
- Header file for dgl_dllscriptfuncs.cpp. Contains all command definitions and script related constants.
- dgl_dllsoundsystem.h
- Header file for dgl_dllsoundsystem.cpp. Contains CSoundSystem, CSoundChannel, CSoundElement, CMP3Element, CMODElement and
CSampleElement classes definitions. It also contains the structure definitions for sound system related structures used
in the API (and also stored in DemoGL_DLL.h) and internally in the CSoundSystem class.
- dgl_dllstartsystem.h
- Header file for dgl_dllstartsystem.cpp. Contains the structure definitions for the startup dat structure used in the API
calls and internally and the structure to pass parsed commandparameter values for screensaver runtypes.
- dgl_dllstartupdialog.h
- Header file for dgl_dllstartupdialog.cpp. Contains the about dialog animation related constants.
- dgl_dllstdafx.h
- Global include file. This file is used to include all .h files. When you add a .h file, simply add it to this file.
- dgl_dllsysconsole.h
- Header file for dgl_dllsysconsole.cpp. Contains the CSysConsole class declaration and all internal used constants and
structure declarations.
- dgl_dlltexture.h
- Header file for dgl_dlltexture.cpp. Contains the CPixelStore class and CTexture class declarations.
- dgl_dlltextureutilfuncs.h
- Header file for dgl_dlltextureutilfuncs.cpp.
- dgl_dlltimelineevent.h
- Header file for dgl_dlltimelineevent.cpp. Contains the CTimeLineEvent and CTLEParameter classes declarations.
- dgl_dlltimelineutilfuncs.h
- Header file for dgl_dlltimelineutilfuncs.cpp.
- dgl_dlltoken.h
- Header file for dgl_dlltoken.cpp. Contains the CToken class declaration.
- dgl_dllutilfuncs.h
- Header file for dgl_dllutilfuncs.cpp.
- resource.h
- This file is generated by VC++. Do not alter this file. Keep in mind that by definition IDI_ICON is equal to 110 and
IDB_APPLOGO is equal to 140.
- src_beautifiers.h
- A header file with some macro's to easy include TODO, FIXME and QUOTE texts that also appear as a warning in the compiler
output. Very handy because you don't have to search the code for all your TODO's. See the header file for usage details
and credits.
Top
7. Small last things to think about.
The library is not perfect and perhaps never will be. Even at release point there are several things that should have been
changed, removed, recoded or added. They're not. Some inconsistencies are documented here, others are not and the reason they're
still in the code is because some line has to be drawn when it's time to quit tweaking and fiddling with the code and release it.
One of the benefits of Open Source is that others, with different views on problems can find perhaps much easier solutions to
algorithms implemented in the library. These people are especially encouraged to modify, or better: increase the quality, of
DemoGL's sourcecode to submit changes and fixes.
DemoGL contains a sloppy, "because you don't know how, you can't crack it" crypting algorithm. With DemoGL this crypting algorithm
is exposed to the public, and makes your crypted material decryptable by others. However you can easily adjust the algo to keep
your own keys safe, to some point. With DemoGL's sourcecode is also the source of Cryde released, the crypter/packer for DemoGL
datafiles. Cryde's sourcecode, an MFC application, is not documented as detailed as DemoGL's code, but still contains lots of
comments to get you started. It's easy to add your own crypting algorithm both to Cryde and to DemoGL to keep your data safe
for copycats.
The DemoGL version 1.3 will be the first of a perhaps long line of releases based on this initial codebase. We hope you will
enjoy the sourcecode and the library's functionality as much as we enjoyed writing the library and cooking up all the ideas and
features.
Now, dive into it!
The DemoGL development team.